
package pf;

import java.io.*;
import java.nio.channels.FileChannel;
import java.util.*;

import basic.*;


public class PF_Manager implements PF_Manager_Interface {

	private PF_BufferMgr pBufferMgr;
	private Hashtable openFiles = new Hashtable(); 
	
	//Constructeur. Initialise les variables d'instance
	public PF_Manager(){

		pBufferMgr = new PF_BufferMgr();
		openFiles = null;

	}

	/*
	 * Cette methode permet de creer un fichier pagine de nom filename et 
	 * d'ecrire les meta-donnees dans l'ente de la premiere page
	 * Un fichier portant ce nom ne devrait pas deja exister
	 * Params: 	String filename: Le nom du fichier a creer
	 * Retour:	PF Error code 
	 */
	public ReturnCode CreateFile(String filename){

		//Creer le fichier ou retoruner un code d'erreur 
		//positif si le fichier existe deja
		File f = new File(filename);
		if (f.exists())
			return ReturnCode.PF_FILEEXIST;

		try {

			f.createNewFile();

			RandomAccessFile ioStream = new RandomAccessFile(f, "rws");

			//Initialisation de l'entete du fichier
			PageNum numPages = new PageNum(1);
			PageNum fisrtFree = new PageNum(0);
			PF_FileHdr hdr = new PF_FileHdr(fisrtFree, numPages);

			ioStream.setLength(Constant.PF_PAGE_SIZE);
			
			int fFree = Integer.parseInt(fisrtFree.toString());
			int nPages = Integer.parseInt(numPages.toString());
			ioStream.writeInt(fFree);
			ioStream.writeInt(nPages);

			ioStream.close();

		} catch (FileNotFoundException e) {

			return ReturnCode.PF_CANNOTCREATEFILE;

		}catch (IOException e) {

			return ReturnCode.PF_CANNOTCREATEFILE;
		}

		return ReturnCode.PF_SUCCESS;
	}


	/*
	 * Cette methode permet d'ouvrir un fichier pagine de nom filename 
	 * Un fichier portant ce nom devrait deja exister
	 * Params: 	String filename: Le nom du fichier a ouvrir
	 * Output; Un objet PF_FileHandle pointant sur ce fichier
	 * Retour:	PF Error code 
	 */
	public ReturnCode OpenFile(String filename, PF_FileHandle fileHandle) throws IOException {

		//On verifie que le fichier existe
		File f = new File(filename);
		if (!f.exists())
			return ReturnCode.PF_FILENOTFOUND;

		//On verifie que le fichier n'est pas deja ouvert
		if (openFiles.get(filename).equals(fileHandle))
			return ReturnCode.PF_FILEOPEN;

		//On ouvre le fichier et on recupere l'objet 
		//PF_FileHandle relatif a ce fichier
		fileHandle = getFileHandle(f);

		//On marque le fichier ouvert
		openFiles.put(fileHandle.getFileChanel(), fileHandle);

		return ReturnCode.PF_SUCCESS;
	}


	/*
	 * Cette methode permet de fermer le fichier de nom filename 
	 * Le fichier devrait avoir ete prealablement ouver par OpenFile()
	 * Params: 	PF_FileHandle fileHandle: L'objet PF_FileHandle point vers ce fichier
	 * Output:
	 * Retour:	PF Error code 
	 */
	public ReturnCode CloseFile(PF_FileHandle fileHandle) {


		//On verifie que le fichier a bien ete ouvert
		if (!openFiles.contains(fileHandle))
			return ReturnCode.PF_CLOSEDFILE;

		// On flush toutes les pages de ce fichier du buffer pool et on met a jour le header
		fileHandle.flushPages();

		//On recupere l'objet FileChanel relatif a ce fichier
		FileChannel fc = fileHandle.getFileChanel();
		if ( fc == null )
			return ReturnCode.PF_CLOSEDFILE;

		// On ferme le fichier
		try {
			fc.close();
		} catch (IOException e) {

			return ReturnCode.PF_EOF;
		}

		//On retire le pointeur PF_FilrHandle de la liste des fichiers ouverts
		openFiles.remove(fileHandle.getFileChanel());

		return ReturnCode.PF_SUCCESS;
	}


	//Supprime le fichier de nom filename
	public ReturnCode DestroyFile(String filename) {

		File f = new File(filename);

		//On verifie que le fichier existe
		if (!f.exists())
			return ReturnCode.PF_FILENOTFOUND;

		//On verifie que le fichier n'est pas deja ouvert
		if (openFiles.containsKey(filename))
			return ReturnCode.PF_FILEOPEN;

		//On supprime le fichier
		if (!f.delete())
			return ReturnCode.PF_FAILEDTODELETEFILE;

		return ReturnCode.PF_SUCCESS;

	}


	/*/
	// ClearBuffer
	//
	// Desc: Remove all entries from the buffer manager.
	//	       This routine will be called via the system command and is only
	//	       really useful if the user wants to run some performance
	//	       comparison starting with an clean buffer.
	// In:   Nothing
	// Out:  Nothing
	// Ret:  Returns the result of PF_BufferMgr::ClearBuffer
	//	       It is a code: 0 for success, something else for a PF error.
	/*/
	public ReturnCode clearBuffer()
	{
		return pBufferMgr.clearBuffer();
	}

	/*/
	// PrintBuffer
	//
	// Desc: Display all of the pages within the buffer.
	//	       This routine will be called via the system command.
	// In:   Nothing
	// Out:  Nothing
	// Ret:  Returns the result of PF_BufferMgr::PrintBuffer
	//	       It is a code: 0 for success, something else for a PF error.
	/*/
	public ReturnCode printBuffer()
	{
		return pBufferMgr.printBuffer();
	}

	/*
	// ResizeBuffer
	//
	// Desc: Resizes the buffer manager to the size passed in.
	//	       This routine will be called via the system command.
	// In:   The new buffer size
	// Out:  Nothing
	// Ret:  Returns the result of PF_BufferMgr::ResizeBuffer
	//	       It is a code: 0 for success, PF_TOOSMALL when iNewSize
	//	       would be too small.
	/*/
	public ReturnCode ResizeBuffer(int iNewSize)
	{
		return pBufferMgr.ResizeBuffer(iNewSize);
	}

	//------------------------------------------------------------------------------
	// Three Methods for manipulating raw memory buffers.  These memory
	// locations are handled by the buffer manager, but are not
	// associated with a particular file.  These should be used if you
	// want memory that is bounded by the size of the buffer pool.
	//
	// The PF_Manager just passes the calls down to the Buffer manager.
	//------------------------------------------------------------------------------


	public ReturnCode GetBlockSize(int length)
	{
		return pBufferMgr.GetBlockSize(length);
	}

	//Alloue une page (un block) a un fichier
	public ReturnCode AllocateBlock(byte[] buffer)
	{
		return pBufferMgr.AllocateBlock(buffer);
	}

	//Supprime une page (un block) d'un fichier
	public ReturnCode DisposeBlock(byte[] buffer)
	{
		return pBufferMgr.DisposeBlock(buffer);
	}


	/*
	 * Cette methode initialise et retourne l'objet PF_FileHandle
	 * relatif au fichier f en parametre.
	 * Params: File f, l'objet File correspondant
	 * Return: PF_FileHandle fh
	 */ 	
	private PF_FileHandle getFileHandle(File f) throws IOException{

		PF_FileHdr hdr = null;
		PF_BufferMgr pBufMgr = null;

		//On cree un fileChannel qui pointera vers ce fichier pour
		//des operation de lecture et ecriture futures
		RandomAccessFile ioStream = new RandomAccessFile(f, "rws");
		FileChannel fc = ioStream.getChannel();
		
		//On lit l'entete du fichier
		ioStream.seek(0);
		hdr.setNumPages(new PageNum(ioStream.readInt()));
		hdr.setFirstFree(new PageNum(ioStream.readInt()));

		//On initialise le gestionaire de memoire
		pBufMgr = new PF_BufferMgr();
		
		return new PF_FileHandle(pBufMgr, hdr, true, true, fc);

	}

	//Print the buffer pool
	public ReturnCode PrintBuffer() {
		
		return pBufferMgr.printBuffer();
	}

	//Clear pages in buffer
	public ReturnCode ClearBuffer() {
		
		return pBufferMgr.clearBuffer();
	}


}
